<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>G6 思维导图 - Mock 示例</title>
    <script src="https://unpkg.com/@antv/g6@4.8.22/dist/g6.min.js"></script>
    <style>
        body {
            margin: 0;
            padding: 0;
            font-family: sans-serif;
        }

        #container {
            width: 100vw;
            height: 100vh;
            position: relative;
            overflow: hidden;
        }

        .toolbar {
            position: absolute;
            top: 10px;
            right: 10px;
            background: white;
            border-radius: 4px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            z-index: 10;
            list-style: none;
            padding: 8px 0;
            margin: 0;
            display: flex;
            flex-direction: column;
            gap: 6px;
        }

        .toolbar li {
            cursor: pointer;
            padding: 6px 12px;
            font-size: 14px;
            color: #333;
            user-select: none;
        }

        .toolbar li:hover {
            background-color: #f5f5f5;
        }

        .toolbar li:last-child {
            text-align: center;
            border-top: 1px solid #eee;
            margin-top: 4px;
            padding-top: 8px;
            font-size: 16px;
        }

        .g6-tooltip {
            border: 1px solid #e2e2e2;
            border-radius: 4px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
        }

        .menubox {
            width: 120px;
            background: white;
            border: 1px solid #ddd;
            border-radius: 4px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            overflow: hidden;
        }

        .menubox_item {
            padding: 8px 12px;
            cursor: pointer;
            font-size: 14px;
        }

        .menubox_item:hover:not(.menubox_item_disabled) {
            background: #f0f7ff;
        }

        .menubox_item_disabled {
            color: #aaa;
            cursor: not-allowed;
            opacity: 0.6;
        }
    </style>
</head>

<body>
    <div id="container">
        <ul class="toolbar">
            <li data-cmd="zoomOut">🔍 放大</li>
            <li data-cmd="zoomIn">🔎 缩小</li>
            <li data-cmd="fitCenter">🎯 居中</li>
            <li data-cmd="fitView">📊 适应</li>
            <li data-cmd="fullscreen">⛶ 全屏</li>
        </ul>
    </div>

    <script>
        // =======================
        // Mock 后端数据 (模拟 transformToG6Data 的输出)
        // =======================
        const mockData = {
            id: 'root',
            name: '系统架构中心',
            inst_name: 'sys_center',
            code: 'SYS_001',
            data_type: 'root',
            children: [
                {
                    id: 'model_1',
                    name: '用户管理模块',
                    inst_name: 'user_model',
                    code: 'UM_001',
                    data_type: 'model',
                    model_code: 'USER_M',
                    tableQuery: { total: 25 },
                    transferStation: true,
                    children: [
                        {
                            id: 'field_1_1',
                            name: '用户名',
                            inst_name: 'username',
                            code: 'F_001',
                            data_type: 'field',
                            isLeaf: true
                        },
                        {
                            id: 'field_1_2',
                            name: '邮箱',
                            inst_name: 'email',
                            code: 'F_002',
                            data_type: 'field',
                            isLeaf: true
                        }
                    ]
                },
                {
                    id: 'model_2',
                    name: '订单处理系统非常长的名字展示省略号效果',
                    inst_name: 'order_model',
                    code: 'OM_001',
                    data_type: 'model',
                    model_code: 'ORDER_M',
                    tableQuery: { total: 45 },
                    children: [
                        {
                            id: 'sub_2_1',
                            name: '创建订单',
                            inst_name: 'create_order',
                            code: 'SO_001',
                            data_type: 'model',
                            model_code: 'CREATE_O',
                            tableQuery: { total: 10 },
                            children: []
                        },
                        {
                            id: 'sub_2_2',
                            name: '支付流程',
                            inst_name: 'payment_flow',
                            code: 'SO_002',
                            data_type: 'model',
                            model_code: 'PAY_F',
                            tableQuery: { total: 35 },
                            children: []
                        }
                    ]
                },
                {
                    id: 'model_3',
                    name: '日志监控平台',
                    inst_name: 'log_monitor',
                    code: 'LM_001',
                    data_type: 'model',
                    model_code: 'LOG_M',
                    tableQuery: { total: 100 },
                    children: []
                }
            ]
        };

        // =======================
        // 工具函数：文本截断
        // =======================
        function formatText(text, maxWidth, fontSize) {
            const charWidth = fontSize * 0.5;
            const maxChars = Math.floor(maxWidth / charWidth);
            if (text.length <= maxChars) return text;
            return text.substring(0, maxChars - 3) + '...';
        }

        // =======================
        // 注册自定义节点：card-node
        // =======================
        G6.registerNode('card-node', {
            draw(cfg, group) {
                const width = cfg.size[0] || 120;
                const height = cfg.size[1] || 40;

                let transferStation = false;
                if (cfg.transferStation) {
                    transferStation = true;
                }

                // 背景矩形（非中转站才绘制）
                if (!transferStation) {
                    group.addShape('rect', {
                        attrs: {
                            x: -width / 2,
                            y: -height / 2,
                            width,
                            height,
                            stroke: cfg.isRoot ? '#1890ff' : '#5B8FF9',
                            radius: 2,
                            fill: '#fff'
                        },
                        name: 'main-box'
                    });
                }
                if (transferStation) {
                    group.addShape('rect', {
                        attrs: {
                            x: -width / 2,
                            y: -height / 2 + 10,
                            width,
                            height: height / 2,
                            stroke: '#ccc',
                            radius: 5,
                            fill: '#ccc'
                        },
                        name: 'main-box'
                    });
                }
                if (transferStation) {
                    // 标题文本
                    group.addShape('text', {
                        attrs: {
                            textBaseline: 'top',
                            x: -width / 2 + 8,
                            y: transferStation ? -height / 4 + 4 : -height / 2 + 4,
                            text: formatText(cfg.location || cfg.name || cfg.id, width - 10, 12),
                            fill: 'black',
                            fontSize: 12
                        },
                        name: 'title-text'
                    });
                }
                // 标题栏
                if (!transferStation) {
                    group.addShape('rect', {
                        attrs: {
                            x: -width / 2,
                            y: transferStation ? -height / 4 - 5 : -height / 2,
                            width,
                            height: transferStation ? height / 2 + 10 : height / 2,
                            fill: 'rgb(229, 236, 249)',
                            radius: [2, 2, 0, 0]
                        },
                        name: 'title-box'
                    });
                }
                if (!transferStation) {
                    // 标题文本
                    group.addShape('text', {
                        attrs: {
                            textBaseline: 'top',
                            x: -width / 2 + 8,
                            y: transferStation ? -height / 4 + 4 : -height / 2 + 4,
                            text: formatText(cfg.location || cfg.name || cfg.id, width - 10, 12),
                            fill: 'black',
                            fontSize: 12
                        },
                        name: 'title-text'
                    });
                }

                // 折叠图标（有子节点时）
                if (cfg.children && cfg.children.length > 0) {
                    const marker = group.addShape('marker', {
                        attrs: {
                            x: width / 2,
                            y: 0,
                            r: 6,
                            cursor: 'pointer',
                            symbol: cfg.collapsed ? G6.Marker.expand : G6.Marker.collapse,
                            stroke: '#666',
                            lineWidth: 1,
                            fill: '#fff'
                        },
                        name: 'collapse-icon'
                    });
                    marker.set('capture', true); // 允许事件捕获
                }

                // 描述文本（仅非中转站）
                if (!transferStation) {
                    if (!transferStation && cfg.name) {
                        group.addShape('text', {
                            attrs: {
                                textBaseline: 'top',
                                x: -width / 2 + 8,
                                y: -height / 2 + 24,
                                text: formatText(cfg.name, width - 10, 12),
                                fill: 'rgba(0,0,0,0.6)',
                                fontSize: 12
                            },
                            name: 'desc-text'
                        });
                    }
                }
                return group;
            },
            setState(name, value, item) {
                if (name === 'collapsed') {
                    const group = item.getContainer();
                    const icon = group.find((ele) => ele.get('name') === 'collapse-icon');
                    if (icon) {
                        const symbol = value ? G6.Marker.expand : G6.Marker.collapse;
                        icon.attr('symbol', symbol);
                    }
                }

                if (name === 'selected') {
                    const group = item.getContainer();
                    const main = group.find(ele => ele.get('name') === 'main-box');
                    if (main) main.attr({ stroke: value ? '#40a9ff' : '#5B8FF9', lineWidth: value ? 2 : 1 });
                }
            }
        }, 'single-node');

        // =======================
        // 创建 Graph 实例
        // =======================
        const container = document.getElementById('container');
        const width = container.scrollWidth;
        const height = container.scrollHeight || window.innerHeight;

        const graph = new G6.TreeGraph({
            container: 'container',
            width,
            height,
            modes: {
                default: ['drag-canvas', 'zoom-canvas', 'click-select'],
                altSelect: [
                    {
                        type: 'click-select',
                        trigger: 'alt',
                    },
                    'drag-node',
                ],
                nodeStateStyles: { selected: { stroke: '#40a9ff', lineWidth: 2 } }
            },
            defaultNode: {
                type: 'card-node',
                size: [120, 40],
                isRoot: false
            },
            defaultEdge: {
                type: 'cubic-horizontal',
                style: {
                    // endArrow: {
                    //     path: G6.Arrow.triangle(10, 10, 5),
                    //     fill: '#ccc'
                    // },
                    endArrow: true,
                    stroke: '#ccc'
                }
            },
            layout: {
                type: 'mindmap',
                direction: 'LR',
                dropCap: false,
                indent: 200,
                getHeight: () => 60,
                getWidth: () => 16,
                getVGap: () => 10,
                getHGap: () => 100,
                getSide: () => 'right'
            },
            plugins: [
                // 网格
                new G6.Grid({
                    cellSize: 10,
                    stroke: '#e2e2e2',
                    lineDash: [2, 2]
                }),
                // Tooltip
                new G6.Tooltip({
                    offsetX: 0.1,
                    offsetY: 45,
                    fixToNode: [0.5, 0],
                    itemTypes: ['node'],
                    getContent(e) {
                        const model = e.item.getModel();
                        if (model.data_type === 'field') {
                            return '<div>关联的模型</div>';
                        }
                        const tipHtml = model.tableQuery?.total > 30
                            ? "<div style='color:grey;padding-bottom:2px'>实例数量暂只展示30条</div>"
                            : '';
                        return `
              <div style="width: 180px;">
                ${tipHtml}
                <div>唯一标识：${model.inst_name || '-'}</div>
                <div>名称：${model.name || '-'}</div>
              </div>`;
                    }
                }),
                // 右键菜单
                new G6.Menu({
                    offsetX: 10,
                    offsetY: 10,
                    itemTypes: ['node'],
                    getContent(e) {
                        const model = e.item.getModel();
                        const isRoot = model.data_type === 'root';
                        const isField = model.data_type === 'field';

                        const menu = document.createElement('div');
                        menu.className = 'menubox';
                        menu.innerHTML = `
              <div class="menubox_item ${isRoot || isField ? 'menubox_item_disabled' : ''}" data-key="1">查看详情</div>
              <div class="menubox_item ${isRoot || isField ? 'menubox_item_disabled' : ''}" data-key="2">查看连接关系</div>
            `;
                        return menu;
                    },
                    handleMenuClick(target, item) {
                        const key = target.getAttribute('data-key');
                        const model = item.getModel();

                        if (target.classList.contains('menubox_item_disabled')) return;

                        if (key === '1') {
                            const url = `?model_code=${model.model_code}&unique=${model.inst_name}&code=${model.code}&name=${encodeURIComponent(model.name)}&SERVER_name=10.80.19.102`;
                            window.open(url, '_blank');
                        } else if (key === '2') {
                            console.log('查看连接关系', {
                                model_code: model.model_code,
                                unique: model.inst_name,
                                code: model.code,
                                depth: 1
                            });
                            alert(`查看连接关系：${model.name}`);
                        }
                    }
                })
            ]
        });

        // =======================
        // 设置根节点 isRoot = true
        // =======================
        const dataWithRoot = JSON.parse(JSON.stringify(mockData));
        dataWithRoot.isRoot = true;
        dataWithRoot.location = dataWithRoot.name;

        // 处理递归添加 location 字段（用于标题显示）
        function processLocation(data) {
            data.location = data.name;
            if (data.children) {
                data.children.forEach(processLocation);
            }
        }
        processLocation(dataWithRoot);

        // 加载数据
        graph.data(dataWithRoot);
        graph.render();

        // 布局完成后居中
        graph.once('afterlayout', () => {
            graph.fitCenter();
        });

        // =======================
        // 工具栏事件绑定
        // =======================
        const toolbar = document.querySelector('.toolbar');
        toolbar.addEventListener('click', (e) => {
            const cmd = e.target.dataset.cmd;
            if (!cmd) return;

            switch (cmd) {
                case 'zoomIn':
                    graph.zoom(1.2);
                    break;
                case 'zoomOut':
                    graph.zoom(0.8);
                    break;
                case 'fitCenter':
                    graph.fitCenter();
                    break;
                case 'fitView':
                    graph.fitView([20, 20]);
                    break;
                case 'fullscreen':
                    if (!document.fullscreenElement) {
                        document.body.requestFullscreen();
                        setTimeout(() => graph.changeSize(window.innerWidth, window.innerHeight), 300);
                    } else {
                        document.exitFullscreen();
                        setTimeout(() => graph.changeSize(container.offsetWidth, container.offsetHeight), 300);
                    }
                    break;
            }
        });

        // 监听窗口 resize
        window.addEventListener('resize', () => {
            if (!graph || graph.destroyed) return;
            const w = container.scrollWidth || window.innerWidth;
            const h = container.scrollHeight || window.innerHeight;
            graph.changeSize(w, h);
        });

        // =======================
        // 节点点击：折叠/展开
        // =======================
        graph.on('node:click', (e) => {
            const shape = e.target;
            if (shape.get('name') === 'collapse-icon') {
                const node = e.item;
                const model = node.getModel();
                model.collapsed = !model.collapsed;
                graph.setItemState(node, 'collapsed', model.collapsed);
                graph.layout();
            }
        });
    </script>
</body>

</html>